package modifyx

import (
	"gitee.com/zhongguo168a/gocodes/datax/convertx"
	"strconv"
)

func newArray() (obj *Array) {
	obj = &Array{}
	obj.disablePoolField = []bool{}
	return
}

// 修正对象
// 如果需要在运行时，替换源的数据，可通过设置runtime实现
type Array struct {
	source *Source
	// 字段的路径
	path string
	// 在父对象中的字段名字
	field string
	// 父数据
	parent map[string]interface{}
	// 缓存的map/array/struct等对象, 避免重复创建对象
	temps []interface{}
	//
	creator func(obj IObject) IObject
	// 当前修正对象指向的源数据
	sourceData []interface{}
	// 运行时设置的数据
	runtime []interface{}
	// 最终的数据, 采用不同类型的原因是，初始值
	ints    []int
	uints   []uint
	floats  []float64
	strings []string
	bools   []bool
	// 需要使用修正池修正的字段
	// 用于性能优化，避免进入修正池流程。
	disablePoolField []bool
	//
	disabelPool bool
}

func (s *Array) initData() {
	s.ints = make([]int, len(s.sourceData), len(s.sourceData))
	s.uints = make([]uint, len(s.sourceData), len(s.sourceData))
	s.floats = make([]float64, len(s.sourceData), len(s.sourceData))
	s.strings = make([]string, len(s.sourceData), len(s.sourceData))
	s.bools = make([]bool, len(s.sourceData), len(s.sourceData))
	s.temps = make([]interface{}, len(s.sourceData), len(s.sourceData))
	s.disablePoolField = make([]bool, len(s.sourceData), len(s.sourceData))

	for key, val := range s.sourceData {
		switch v := val.(type) {
		case int:
			s.ints[key] = v
		case uint:
			s.uints[key] = v
		case float64:
			s.floats[key] = v
		case bool:
			s.bools[key] = v
		case string:
			s.strings[key] = v
		}
	}
}

func (s *Array) Dispose() {
	s.sourceData = nil
	s.ints = nil
	s.floats = nil
	s.strings = nil
	s.bools = nil
	s.parent = nil
	s.temps = nil
	s.source = nil
}

func (s *Array) getOriginBool(field int) bool {
	// 运行时
	if s.runtime != nil {
		if rval := s.runtime[field]; rval != nil {
			return rval.(bool)
		}
	}

	return convertx.AnyToBool(s.sourceData[field])
}

func (s *Array) getOriginFloat(field int) float64 {
	// 运行时
	if s.runtime != nil {
		if rval := s.runtime[field]; rval != nil {
			return rval.(float64)
		}
	}

	return convertx.AnyToFloat64(s.sourceData[field])
}
func (s *Array) getOriginInt(field int) int {
	// 运行时
	if s.runtime != nil {
		if rval := s.runtime[field]; rval != nil {
			return rval.(int)
		}
	}
	return convertx.AnyToInt(s.sourceData[field])
}

// 修正后的整型
func (s *Array) RefInt(field int) int {
	origin := s.getOriginInt(field)
	s.ints[field] = origin
	// 池修正
	if !s.disabelPool {
		if !s.disablePoolField[field] {
			pools := s.source.pools
			if pools != nil {
				lastkey := s.path + "/" + strconv.Itoa(field)
				if pools.IsPropChanged(lastkey) {
					// 更新属性
					s.ints[field] = pools.GetNewPropInt(lastkey, origin)
				}
			}
		}
	}

	return s.ints[field]
}

// 修正后的整型
func (s *Array) RefBool(field int) bool {
	origin := s.getOriginBool(field)
	s.bools[field] = origin
	// 池修正
	if !s.disabelPool {
		if !s.disablePoolField[field] {
			pools := s.source.pools
			if pools != nil {
				lastkey := s.path + "/" + strconv.Itoa(field)
				if pools.IsPropChanged(lastkey) {
					// 更新属性
					s.bools[field] = pools.GetNewPropBool(lastkey, origin)
				}
			}
		}
	}

	return s.bools[field]

}

// 修正后的浮点型
func (s *Array) RefFloat(field int) float64 {
	origin := s.getOriginFloat(field)
	s.floats[field] = origin
	// 池修正
	if !s.disabelPool {
		if !s.disablePoolField[field] {
			pools := s.source.pools
			if pools != nil {
				lastkey := s.path + "/" + strconv.Itoa(field)
				if pools.IsPropChanged(lastkey) {
					// 更新属性
					s.floats[field] = pools.GetNewPropFloat(lastkey, origin)
				}
			}
		}
	}

	return s.floats[field]
}

// 获取原始数据
func (s *Array) RefSourceData() []interface{} {
	return s.sourceData
}

func (s *Array) RefUint(index int) (val uint) {
	return uint(s.RefInt(index))
}

func (s *Array) RefString(index int) (val string) {
	return s.strings[index]
}

func (s *Array) RefObject(index int) (r IObject) {
	iobj := s.temps[index]
	if iobj == nil {
		obj := newObject()
		obj.source = s.source
		obj.field = strconv.Itoa(index)
		obj.path = s.path + "/" + obj.field
		obj.temps = map[string]interface{}{}
		//
		m := s.sourceData[index]
		if m != nil {
			obj.sourceData = m.(map[string]interface{})
			obj.initData()
		}

		creator := s.creator
		if creator != nil {
			iobj = creator(obj)
		} else {
			iobj = obj
		}

		s.temps[index] = iobj
	}

	r = iobj.(IObject)
	return
}

func (s *Array) RefLength() int {
	return len(s.sourceData)
}
